package com.example.demo.controller;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.example.demo.exception.BusyException;
import com.example.demo.mapper.*;
import com.example.demo.aop.Idempotents;
import com.example.demo.entity.*;
import com.example.demo.service.AprioriService;
import com.example.demo.service.ClusterService;
import com.example.demo.service.PublishService;
import com.example.demo.util.AssertUtil;
import com.example.demo.util.DownloadUtil;
import com.example.demo.util.POIUtil;
import com.example.demo.util.TransactionUtil;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/data")
public class ImportDateController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private StudentMapper stum;
    @Autowired
    private ScoreMapper sm;
    @Autowired
    private UploadMapper um;
    @Autowired
    private Score_CopyMapper scm;
    @Autowired
    private ClusterService cs;
    @Autowired
    private AprioriService as;
    @Autowired
    private ReleatimeRulesMapper rrm;
    @Autowired
    private  WarningInfoMapper wim;
    @Autowired
    private  CollegesMapper cmmm;
    @Autowired
    private  PublishService ps;
    @Autowired
    private  TeacherMapper tm;
    @Autowired
    private  NeedMapper nm;
    @Autowired
    private GradeMapper gradeMapper;

    @Autowired
    private CourseTypeMapper courseTypeMapper;

    @Resource(name = "common")
    private Executor commonExecutor;

    private static final Map<String, Double> systemInfoCache = new HashMap<>();

    @Autowired
    private TransactionUtil transactionUtil;

    private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    private static final SystemInfo systemInfo = new SystemInfo();//获取硬件信息
    //获取CPU
    private static final CentralProcessor processor = systemInfo.getHardware().getProcessor();

    static {
        // 启动定时任务，每隔1秒更新一次系统信息
        executorService.scheduleAtFixedRate(ImportDateController::updateSystemInfo, 0, 1, TimeUnit.SECONDS);
    }

    @PostMapping("/import")
    public String importData(MultipartFile file,@RequestParam("user") String user,HttpServletRequest request) throws IOException {
        List<Score> scoreList = POIUtil.excelScore(file);
        List<Score_copy> list=new ArrayList<>();
        int flag=um.selectLast().getId()+1;
        Upload u=new Upload(flag,user,new Date(System.currentTimeMillis()),"待审核");
        System.out.println(flag);
        for (Score s :scoreList){
            Score_copy ss=new Score_copy(s.getFlag(),s.getId(),s.getName(),s.getMajor(),s.getClassi(),s.getCourse(),s.getTeachername(),s.getGredit(),s.getEscore(),s.getAscore(),s.getFscore(),s.getTerm(),flag);
            list.add(ss);
        }
        redisTemplate.opsForValue().set("score:upload:cache"+flag,JSONObject.toJSONString(list),30, TimeUnit.MINUTES);
        TransactionStatus transactionStatus = transactionUtil.beginTransaction();
        int ii;
        int i;
        try {
             ii = scm.MultiAdd(list);
             i = um.register(u);
            transactionUtil.commitTransaction(transactionStatus);
//        System.out.println(user);
        } catch (Exception e) {
            transactionUtil.rollbackTransaction(transactionStatus);
            throw new BusyException(e.getMessage());
        }
        if (i > 0 && ii > 0) {
            System.out.println("1111111");
            return "ok";
        } else return "iok";
    }

    @PostMapping("/importNewNeed")
    public void importDataNewNeed(MultipartFile file,@RequestParam("user") String user,HttpServletResponse response) throws IOException {
        List<Colleges> scoreList = POIUtil.excelNewNeed(file);
        List<Need> needs = POIUtil.excelNewNeed1(file);
        List<Grade> grades = POIUtil.excelGrade(file);
        validateFieldNotNull(scoreList, needs, grades);
        Set<String> collegeSet = CollectionUtils.emptyIfNull(needs).stream().map(Need::getCollege).collect(Collectors.toSet());
        List<CourseType> courseTypes = courseTypeMapper.selectByCollege(Lists.newArrayList(collegeSet));
        Set<Integer> courseTypeFlagSet = CollectionUtils.emptyIfNull(courseTypes).stream().map(CourseType::getTypeFlag).collect(Collectors.toSet());
        boolean isExistCourseType = CollectionUtils.emptyIfNull(needs).stream().anyMatch(need -> courseTypeFlagSet.contains(need.getType()));
        if (!isExistCourseType) throw new BusyException("课程类别不存在，请先创建课程类别");
        TransactionStatus transactionStatus = transactionUtil.beginTransaction();
        try {
            int i = cmmm.add(scoreList);
            int ii = nm.addMu(needs);
            int iii = gradeMapper.addMu(grades);
            transactionUtil.commitTransaction(transactionStatus);
//        System.out.println(user);
            if (i > 0 && ii > 0 && iii > 0) {
                System.out.println("1111111");
                response.getWriter().write(JSONObject.toJSONString(Message.success("success upload")));
            } else response.getWriter().write(JSONObject.toJSONString(Message.failure("failure upload")));
        } catch (Exception e) {
            transactionUtil.rollbackTransaction(transactionStatus);
            throw new BusyException(e.getMessage());
        }
    }

    private void validateFieldNotNull(List<Colleges> scoreList, List<Need> needs, List<Grade> grades) {
        for (Colleges colleges : scoreList) {
            batchCheckFieldNotNull(colleges.getCollege(),colleges.getComputerLevel(),
                    colleges.getEnglishTestFour(),colleges.getVersion(),colleges.getTotalCredit(),colleges.getGraduationGredit());
        }
        for (Need need: needs) {
            batchCheckFieldNotNull(need.getNeedCourse(),need.getGret(),need.getTerm(),need.getMajor(),need.getType());
        }
        for (Grade grade : grades) {
            batchCheckFieldNotNull(grade.getCollege(), grade.getGrade());
        }
    }

    private void batchCheckFieldNotNull(Object...values) {
        for (Object obj : values) {
            AssertUtil.notNull(obj, "有必填属性为空");
        }
    }

    @PostMapping("/terntodetail")
    public void ternToDetail(HttpServletRequest request, HttpServletResponse response) throws IOException {
        int id = Integer.valueOf(request.getParameter("id"));
        System.out.println(id);
        List<Score_copy> scoreByUploadId = scm.findScoreByUploadId(id);
        if(scoreByUploadId!=null)
        response.getWriter().write(JSONObject.toJSONString(Message.success(scoreByUploadId)));
        else  response.getWriter().write(JSONObject.toJSONString(Message.failure("atain failure")));
    }

    @GetMapping("/all")
    public void all(HttpServletRequest request, HttpServletResponse response) throws IOException {
        List<Upload> alll = um.findAlll();
        if(alll!=null)
            response.getWriter().write(JSONObject.toJSONString(Message.success(alll)));
        else  response.getWriter().write(JSONObject.toJSONString(Message.failure(" failure")));
    }

    @GetMapping("/alls")
    public void alls(HttpServletRequest request, HttpServletResponse response) throws IOException {
        List<Student> students = stum.findAlllStu();
        List<Teacher> teachers=tm.findAllTea();
        Map<String,Integer> map=new HashMap<>();
        for(Student student:students){
            String college = student.getCollege();
            String grade = student.getGrade();
            if (!map.containsKey(college+grade)) {
                map.put(college+grade,1);
            }
            map.put(college+grade,map.get(college+grade)+1);
        }
        for(Teacher t:teachers){
            String college = t.getCollege();
            if (!map.containsKey(college)) {
                map.put(college+"辅导员",1);
            }
            map.put(college+"辅导员",map.get(college+"辅导员")+1);
        }
        response.getWriter().write(JSONObject.toJSONString(Message.success(map)));
    }

    @GetMapping("/sys")
    public void sys(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.getWriter().write(JSONObject.toJSONString(Message.success(systemInfoCache)));
    }

    private static void updateSystemInfo() {
        long[] prevTicks = processor.getSystemCpuLoadTicks();//获取当前CPU的系统负载
        try {
            // 等待一段时间，以便获取准确的CPU负载数据
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        systemInfoCache.clear();
        long[] ticks = processor.getSystemCpuLoadTicks();//获取当前CPU的系统负载
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];//比较当前和上一次读取时的时间
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];//计算了CPU处理中断（IRQ）的时间变化
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];//处理软中断（SOFTIRQ）的时间变化
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];//时间盗用损失的1时间
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];//计算CPU在系统空间执行的时间变化
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];//计算了CPU在用户空间执行的时间变化
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];//计算CPU在等待I/O操作完成的时间变化
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];//计算了CPU处于空闲状态的时间变化
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;//得到CPU的总活动时间
        GlobalMemory memory = systemInfo.getHardware().getMemory();
        long totalByte = memory.getTotal();
        long acaliableByte = memory.getAvailable();
        double v = 1.0 - (idle * 1.0 / totalCpu);
        double s=(totalByte-acaliableByte)*1.0/totalByte;
        systemInfoCache.put("Cpu使用率",v);
        systemInfoCache.put("内存使用率",s);
    }

    @PostMapping("/checksuccess")
    @Idempotents
    public void checkSuccess(HttpServletRequest request, HttpServletResponse response) throws IOException {
        List<Score_copy> scoreByUploadId=new ArrayList<>();
        int id = Integer.parseInt(request.getParameter("id"));
        System.out.println(id);
        String state="审核通过";
        scoreByUploadId = JSONArray.parseArray(redisTemplate.opsForValue().get("score:upload:cache" + id), Score_copy.class);
        if(scoreByUploadId==null) {
            scoreByUploadId = scm.findScoreByUploadId(id);
            redisTemplate.opsForValue().set("score:upload:cache"+id,JSONObject.toJSONString(scoreByUploadId),30, TimeUnit.MINUTES);
        }
        List<Score> list=new ArrayList<>();
        Score_copy firstScore = scoreByUploadId.get(0);

        for (Score_copy s :scoreByUploadId){
            Score ss=new Score(s.getFlag(),s.getId(),s.getName(),s.getMajor(),s.getClassi(),s.getCourse(),s.getTeachername(),s.getGredit(),s.getEscore(),s.getAscore(),s.getFscore(),s.getTerm());
            list.add(ss);
        }
        List<Score_copy> finalScoreByUploadId = scoreByUploadId;
        List<Integer> needAnalyze = finalScoreByUploadId.stream().filter(Objects::nonNull).map(Score_copy::getId).distinct().collect(Collectors.toList());
        commonExecutor.execute(() -> {
            String term=firstScore.getTerm();
            String college=stum.selectStuById(firstScore.getId()).getCollege();
            Set<Cluster> clusters = cs.initData(term, needAnalyze);
            Map<String, Double> stringDoubleMap = as.initData(clusters, term);
            for (Map.Entry<String, Double> entry : stringDoubleMap.entrySet()) {
                String[] split = entry.getKey().split("->");
                ReleationRules r = new ReleationRules(college,split[0], split[1], term, new Date(System.currentTimeMillis()));
                int add = rrm.add(r);
                if (add > 0) {
                    System.out.println("规则插入成功");
                } else System.out.println("规则插入失败");
            }
            List<ReleationRules> releationRules = rrm.selectByCollegeAndTerm(college, term);
            for(Score_copy s : finalScoreByUploadId){
                if(s.getFscore()<60){
                    String relatedFailedCourses = getRelatedFailedCourses(s.getCourse(), releationRules,s.getTerm(),s.getMajor(),stum.selectStuById(s.getId()).getGrade());
                    String detail= "学生挂了" + s.getCourse() + "，可能还会挂掉以下课程："+relatedFailedCourses;
                    Random random=new Random();
                    int random1= random.nextInt(900000)+1000000;
                    int i = wim.addWarningInfo(new WarningInfos(random1, s.getName(), s.getId(), s.getMajor(), s.getClassi(), 1, detail,term, new Date(System.currentTimeMillis()
                    ), new Date(System.currentTimeMillis()), 0,2));
                    if(i>0){
                        System.out.println("动态预警成功");
                    }else System.out.println("动态预警失败");
                }
            }
        });
        // 开启事务
        TransactionStatus transactionStatus = transactionUtil.beginTransaction();
        try {
            List<List<Score>> partitions = Lists.partition(list, 100);
            partitions.forEach(partition -> {
                sm.MultiAdd(list);
            });
            int ii = um.updateUpload(id, state);
            int i = scm.deleteScoreByUploadId(id);
            if (i > 0 && ii > 0) {
                ps.testEvent();
                transactionUtil.commitTransaction(transactionStatus);
                response.getWriter().write(JSONObject.toJSONString(Message.success("success check")));
            } else response.getWriter().write(JSONObject.toJSONString(Message.failure("check failure")));
        } catch (Exception e) {
            transactionUtil.rollbackTransaction(transactionStatus);
            throw new BusyException(e.getMessage());
        }
    }

    public String atanNeed(String term,String major,String grade){
        String str="";
        int gra=Integer.valueOf(grade.split("级")[0]);
        List<Need> needs = nm.selectByMajor(major);
        for(Need n:needs){
           int v=Integer.valueOf(n.getTerm().split("-")[0]);
            if(n.getTerm().equals(term)&&v<=gra+3){
                str+=n.getNeedCourse();
            }
        }
        return str;
    }

    private  String getRelatedFailedCourses(String failedCourse,List<ReleationRules> releationRules,String term,String major,String grade) {
        Set<String> relatedCourses = new HashSet<>();
        for(ReleationRules r: releationRules){
            if(r.getBeforeneed().contains(failedCourse)||atanNeed(term,major,grade).contains(r.getBeforeneed())){
                String[] split = r.getAfterneed().split(";");
                for(int i=0;i<split.length;i++) {
                    if(!String.join("",relatedCourses).contains(split[i])) {
                        relatedCourses.add(split[i]);
                    }
                }
            }
        }
        return String.join("",relatedCourses);
    }

    @PostMapping("/checkfailure")
    @Idempotents
    public void checkFailure(HttpServletRequest request, HttpServletResponse response) throws IOException {
        int id = Integer.valueOf(request.getParameter("id"));
        System.out.println(id);
        String state="审核不通过";
        int ii = um.updateUpload(id, state);
        int i = scm.deleteScoreByUploadId(id);
        if(ii>0&&i>0){
            response.getWriter().write(JSONObject.toJSONString(Message.success("success failure")));
        }else response.getWriter().write(JSONObject.toJSONString(Message.failure("check failure")));
    }

    @RequestMapping("/download")
    public void download(HttpServletRequest request,HttpServletResponse response){
        DownloadUtil util=new DownloadUtil();
        util.exportExcel(response);
    }
}
